# == Schema Information
# Schema version: 33
#
# Table name: usuarios
#
#  id                        :integer(11)   not null, primary key
#  login                     :string(255)   
#  email                     :string(255)   
#  crypted_password          :string(40)    
#  salt                      :string(40)    
#  created_at                :datetime      
#  updated_at                :datetime      
#  remember_token            :string(255)   
#  remember_token_expires_at :datetime      
#  open_id_url               :string(255)   
#  activation_code           :string(40)    
#  activated_at              :datetime      
#  last_seen_at              :datetime      
#  visible                   :boolean(1)    default(TRUE)
#  admin                     :boolean(1)    
#  avatar                    :string(255)   
#  password_reset_code       :string(40)    
#  new_email                 :string(255)   
#  email_activation_code     :string(40)    
#  quiero_ser_amigo          :boolean(1)    
#  publico                   :boolean(1)    
#  privado                   :boolean(1)    
#

require 'digest/sha1'
class Usuario < ActiveRecord::Base
  # Virtual attribute for the unencrypted password
  attr_accessor :password
  
  validates_email_format_of :email, :message => 'Tiene un formato incorrecto' # plugin email
  validates_confirmation_of :password,                   :if => :password_required?, :message => 'No coincide'
  validates_format_of       :login, :with => /^\w+$/, :message => "Debe ser alfanumérico"
  validates_uniqueness_of   :login, :email, :case_sensitive => false, :message => 'Ya existe'
  validates_length_of       :login,    :within => 3..40, :message => 'Es demasiada corta'
  validates_length_of       :email,    :within => 3..100, :message => 'Es demasiada corta'
  validates_length_of       :password, :within => 6..40, :if => :password_required?, :message => 'Es demasiada corta'
  validates_presence_of     :login, :email, :message => 'No puede estar en blanco'
  validates_presence_of     :password,                   :if => :password_required?, :message => 'No puede estar en blanco'
  validates_presence_of     :password_confirmation,      :if => :password_required?, :message => 'No puede estar en blanco'

  attr_protected :admin
  
  before_save :encrypt_password
  before_create :make_activation_code
  after_create :eliminar_baneo_global, :actualizar_identidad_usuario, :crea_nuevo_perfil
  
  file_column :avatar, :base_url => "images/usuario/avatar", :filename => "avatar", 
              :store_dir =>  ENV["RAILS_ENV"] == "test" ? "spec/fixtures/images/usuario/avatar" : "public/images/usuario/avatar", 
              :magick => {:versions => {:thumb => "65x65", :thumb_medium => '48x48', :thumb_little => "30x30"}}
 
  # Relaciones Principales
  has_one :perfil
  has_many :regalos_remitidos, :class_name => 'Regalo', :foreign_key => "remitente_id"
  has_many :regalos_recibidos, :class_name => 'Regalo', :foreign_key => "invitado_id"  
  has_many :amistades, :foreign_key => "usuario_id"
  has_many :baneos, :foreign_key => "baneador_id"
  has_many :baneados, :through => :baneos, :source => :baneado
  has_many :eventos

  has_many :celebraciones

  with_association_scope  :through => :amistades, :source => :amigo  do
    has_many :amigos_todos
    
    has_many :amigos_aceptados, 
      :conditions => "state = 'aceptado'" 
        
    has_many :amigos_en_espera,
      :conditions => "state = 'pendiente'" 
      
    has_many :amigos_rechazados,
      :conditions => "state = 'rechazado'" 
      
    has_many :amigos_molestados,
      :conditions => "state = 'molestado'" 
      
    has_many :amigos_conectados,
      :conditions => ["last_seen_at > ? and state = ? and visible IS TRUE", (Time.now - 3.minutes).to_s(:db), "aceptado"]
        
    has_many :amigos_accesibles,
        :conditions =>["state = 'aceptado' or (state != 'rechazado' and invitado = true)"]
  end
 
  #relaciones derivadas de los regalos 
  with_association_scope  :foreign_key => "remitente_id", :class_name => 'Regalo'  do
    has_many :regalos_remitidos_aceptados,
      :conditions => { :state => 'aceptado' }
    
    has_many :regalos_remitidos_rechazados,
      :conditions => ['state = ? or state = ?', "rechazado", "expirado"]
      
    has_many :regalos_remitidos_pendientes,
      :conditions => { :state => 'pendiente' }
      
    has_many :regalos_remitidos_expirados,
      :conditions => { :state => 'expirado' }
  end
        
  with_association_scope  :foreign_key => "invitado_id", :class_name => 'Regalo'  do 
    has_many :regalos_recibidos_aceptados,
      :conditions => { :state => 'aceptado' }
      
    has_many :regalos_recibidos_rechazados,
      :conditions => ['state = ? or state = ?', "rechazado", "expirado"]
      
    has_many :regalos_recibidos_pendientes,
      :conditions => { :state => 'pendiente' }
      
    has_many :regalos_recibidos_expirados,
      :conditions => { :state => 'expirado' }
  end 
 
  # Obtiene los ultimos 5 eventos
  def eventos
    eventos = []
    eventos += Evento.find(:all, :conditions => ["visto = ? and usuario_id = ? and tipo != ?", false, self.id, "chat"] ,:limit => 5, :order => "created_at desc")
    if eventos.length < 5
      eventos += Evento.find(:all, :conditions => ["visto = ? and usuario_id = ? and tipo != ?", true, self.id, "chat"], :limit => (5-eventos.size), :order => "created_at desc")
    end
    
    eventos.sort_by{|a| a.created_at }.reverse      
  end
  
  # Si el usuario habia baneado a toda la aplicacion antes de registrarse en el sistema, 
  # eliminarémos dicho baneo en el proceso de registro
  def eliminar_baneo_global
    @baneo = Baneo.find(:first, :conditions => ["email_baneador = ? and baneado_id = ?", self.email, -1])      
    Baneo.delete(@baneo.id) if @baneo != nil
  end
    
  # Si el usuario habia tenido actividad previa al registro, actualizaremos sus acciones 
  # con su nueva ID
  def actualizar_identidad_usuario
    Baneo.update_all("email_baneador = null, baneador_id = #{self.id}", ["email_baneador = ?", self.email])
    Baneo.update_all("email_baneado = null, baneado_id = #{self.id}", ["email_baneado = ?", self.email])
    #Actualizar el regalos de prueba q haya hecho, los q hata recibido sin estar registrado 
    Regalo.update_all("email_remitente = null, remitente_id = #{self.id}", ["email_remitente = ?", self.email])
    Regalo.update_all("email_invitado = null, invitado_id = #{self.id}", ["email_invitado = ?", self.email])
  end

  # En el proceso de registro crearemos automaticamente su registro en perfil
  def crea_nuevo_perfil
    Perfil.create(:usuario_id => id) 
  end
  
  # Porcentaje de regalos aceptados con respecto a los rechazados y pendientes
  def reputacion
    numero_regalos("remitidos", "todos") > 0 ? ((numero_regalos("remitidos", "aceptados").to_f / (numero_regalos("remitidos", "todos"))) * 100).to_i : 0
  end

  # Contador de amigos
    def numero_amigos(estado) #·[aceptados , rechazados, en_espera, todos,  conectados]
      eval "amigos_#{estado}.count"
    end

  # Contador de regalos 
  def numero_regalos(rol, estado) #rol => [remitidos, recibidos], estado => [todos, aceptados, rechazados, pendientes, expirados]
    if estado != "todos"
      eval "regalos_#{rol}_#{estado}.count" 
   else
      eval "regalos_#{rol}.count" 
   end
  end
    
  # Devuelve el número de regalos a un determinado destino, es usado para llevar a 
  # cabo una de las validaciones de regalo
  def numero_regalos_a(estado, destino)
    if !destino.nil?
      if estado == "todos"
        if destino.class == String
          self.regalos_remitidos.count(:conditions =>["email_invitado = ?", destino])
        else
          self.regalos_remitidos.count(:conditions =>["invitado_id = ?", destino || destino.id])
        end
      else
        if destino.class == String
          self.regalos_remitidos.count(:conditions =>["email_invitado = ? and state = ?", destino, estado])
        else
          self.regalos_remitidos.count(:conditions =>["invitado_id = ? and state = ?", destino || destino.id, estado])
        end
      end
    end
  end
  
  
   # Usuarios conectados    
  def conectado?
    last_seen_at ? last_seen_at > (Time.now - 3.minutes) : false
  end
  
  def ultimos_5_amigos
    amigos_aceptados.find(:all, :limit => 5, :order => "usuarios.updated_at DESC")    
  end
  
  def ultimos_3_regalados
    regalos_remitidos.find(:all, :limit => 3, :conditions => ['state = ?', "aceptado"], :order => "created_at DESC")
  end
  
  def ultimos_3_recibidos
    regalos_recibidos.find(:all, :limit => 3, :conditions => ['state = ?', "aceptado"], :order => "created_at DESC")
  end
  
  def regalos_favoritos
    regalos_remitidos.find(:all, :limit => 5, :select => 'COUNT(*) as cuenta, tipo_regalo_id', :group => 'tipo_regalo_id') 
  end
  
  def remitente?(regalo)
    id == regalo.remitente_id
  end
  
  def invitado?(regalo)
    id == regalo.invitado_id
  end
  
  def siguiente_cumpleanios
    cumpleanios = []

    recordatorios = Recordatorio.find(:all, :conditions => ['amigo_id = ?', id])    
    recordatorios.each do |recordatorio|
      celebracion = Celebracion.find_by_usuario_id_and_tipo_celebracion_id(recordatorio.usuario_id,
                    TipoCelebracion.find_by_nombre("El día que nací"), 
                    :select => 'date_format(fecha, "%e") as dia, date_format(fecha, "%c") as mes, usuario_id, fecha')
      cumpleanios << celebracion if celebracion
    end

    cumpleanios = cumpleanios.sort_by { |p| [p.mes.to_i, p.dia.to_i] }
    fecha_actual = Time.now

    if (cumpleanio = (cumpleanios.select{|p| (fecha_actual.month == p.mes.to_i && fecha_actual.day <= p.dia.to_i)} |
                      cumpleanios.select{|p| fecha_actual.month < p.mes.to_i } |
                      cumpleanios.select{|p| fecha_actual.month > p.mes.to_i })).not.blank?
      cumpleanio.first
    else
      nil
    end
  end
  
  # Activates the user in the database.
  def activate
    @activated = true
    self.attributes = {:activated_at => Time.now.utc, :activation_code => nil}
    save(false)
  end

  def activated?
    !! activation_code.nil?
  end

  # Returns true if the user has just been activated.
  def recently_activated?
    @activated
  end 
  
  # Authenticates a user by their login name and unencrypted password.  Returns the user or nil.
  def self.authenticate(login, password)
    u = find :first, :conditions => ['login = ? and activated_at IS NOT NULL', login] # need to get the salt
    u && u.authenticated?(password) ? u : nil
  end

  # Encrypts some data with the salt.
  def self.encrypt(password, salt)
    Digest::SHA1.hexdigest("--#{salt}--#{password}--")
  end

  # Encrypts the password with the user salt
  def encrypt(password)
    self.class.encrypt(password, salt)
  end

  def authenticated?(password)
    crypted_password == encrypt(password)
  end

  def remember_token?
    remember_token_expires_at && Time.now.utc < remember_token_expires_at 
  end

  # These create and unset the fields required for remembering users between browser closes
  def remember_me
    remember_me_for 2.weeks
  end

  def remember_me_for(time)
    remember_me_until time.from_now.utc
  end

  def remember_me_until(time)
    self.remember_token_expires_at = time
    self.remember_token            = encrypt("#{email}--#{remember_token_expires_at}")
    save(false)
  end

  def forget_me
    self.remember_token_expires_at = nil
    self.remember_token            = nil
    save(false)
  end
  
  # Nuevas funcionalidades
  
  def forgot_password
    @forgotten_password = true
    self.make_password_reset_code
  end

  def reset_password
    # First update the password_reset_code before setting the 
    # reset_password flag to avoid duplicate email notifications.
    update_attributes(:password_reset_code => nil)
    @reset_password = true
  end
  
  # Used in user_observer
  def recently_forgot_password?
    @forgotten_password
  end
  
  # Las últimas nuevas funcionalidades (cambiar email)
  def change_email_address(new_email_address)
     @change_email  = true
     self.new_email = new_email_address
     self.make_email_activation_code
  end

  def activate_new_email
    @activated_email = true
    update_attributes(:email=> self.new_email, :new_email => nil, :email_activation_code => nil)
  end

  def recently_changed_email?
     @change_email
  end


  protected
    def make_password_reset_code
      self.password_reset_code = Digest::SHA1.hexdigest( Time.now.to_s.split(//).sort_by {rand}.join )
    end

    # before filter 
    def encrypt_password
      return if password.blank?
      self.salt = Digest::SHA1.hexdigest("--#{Time.now.to_s}--#{login}--") if new_record?
      self.crypted_password = encrypt(password)
    end
    
    def password_required?
      open_id_url.nil? && (crypted_password.blank? || !password.blank?)
    end

    def make_activation_code
      self.activation_code = Digest::SHA1.hexdigest( Time.now.to_s.split(//).sort_by {rand}.join )
    end
    
    def make_email_activation_code
      self.email_activation_code = Digest::SHA1.hexdigest( Time.now.to_s.split(//).sort_by {rand}.join )
    end
end
